<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <script src="https://cdn.jsdelivr.net/npm/vue"></script>
  <link rel="shortcut icon" href="../img/favicon.ico" type="image/x-icon">
  <link rel="stylesheet" href="../css/style.css">
  <title>Object变化侦测</title>
</head>

<body>
  <h2>Object的变化侦测</h2>
  <img src="../img/Object-define-property.png" alt="">
  <h3>什么时侯变化侦测</h3>
  <div>1.Vue中的状态变化时，会通知到组件，组件内部再使用虚拟DOM进行对比</div>
  <div>
    2.Vue中Object和Array采用不同的处理方式
  </div>
  <h3>如何追踪变化</h3>
  <ul>
    <li>
      Object.defineProperty
    </li>
    <li>
      ES6 Proxy
    </li>
    <li>
      由于Object.defineProperty侦测变化存在诸多缺陷，所以VueJS作者说日后会使用Prox重写这部分代码
    </li>
    <li>
      我们之所以要观察数据，其目的时当数据发生变化时，可以<span>通知</span>那些曾经使用了该数据的地方
    </li>
    <li>
      Vue2.0中，模板使用数据，相当于组件使用数据。数据发生变化时，会将通知发送到组件
    </li>
    <li>
      收集依赖：把<span>使用到属性的地方(依赖)</span>收集起来(get中收集)
    </li>
    <li>
      触发依赖：依赖收集完成之后，把之前收集好的依赖<span>循环触发</span>一遍(set中触发)
    </li>
  </ul>

  <h3>依赖收集在哪里？</h3>
  <li>
    
  </li>
  <li>
    依赖收集在对应每个状态new Dep()对象中
  </li>

  <h3>依赖是谁？</h3>
  <li>我们收集的是每一个<span>用到该数据的地方</span></li>
  <li>
    使用这个数据有多种形式，可以是模板中的插值，也可能是用户自己设置的watch监听了这个属性。<span>所以要抽象出来一个单独处理这些情况的类</span>
  </li>
  <li>
    我们封装好类之后，在收集阶段，只需要收集这个封装好的类的<span>实例</span>即可
  </li>
  <li>
    收集的东西，现在起一个名字就叫watcher
  </li>
  <li>
    收集的时候，根据某个状态收集了一个数组。当这个状态变化的时候，在setter中触发所有与这个状态相关的watcker更新，很合理
  </li>

  <h3>什么是watcher?</h3>
  <li>watcher是一个中介的角色，数据变化的时候通知Watcher,然后它再通知其他地方</li>
    <h4>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;watcher类定义了两个功能：</h4>
    <ul>
      <li style="text-indent: 4em;">
        1.读取属性值，触发属性对应的getter,触发Dep的收集，收集的是watcher实例。
      </li>
      <li style="text-indent: 4em;">
        2.<span>调用</span>定义Watcher的时候的<span>回调函数</span>： 用于在对象中触发
      </li>
    </ul>

  <h3>关于Object的问题</h3>
  <li>
    VueJs通过Object.defineProperty来将对象的key转换成getter/setter的形式来追踪变化，但getter/setter只能追踪一个数据是否被修改，而<span>无法追踪新增和删除属性</span>
  </li>
  <li>
    vm.$set解决新增
  </li>
  <li>
    vm.$delete解决删除的问题
  </li>

</body>

<script>
  const object = {}
  function defineProperty(obj, key, val) {
    Object.defineProperty(obj, key, {
      enumerable: true,
      configurable: true,
      get: () => {
        console.log('getter')
        return val
      },
      set: newVal => {
        if (val === newVal) {
          return
        }
        val == newVal
      }
    })
  }

  defineProperty(object, 'a', 20)
  console.log(object.a) // getter 20
  defineProperty(object, 'a', 30)
  console.log(object.a) // getter 30
  object.c = 40
  console.log(object.c) // 40 未触发存取器属性getter

</script>

</html>